package primetest.impl.dao;

import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;

import java.util.List;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import primetest.dto.BedDto;
import primetest.dto.BuildingDto;
import primetest.dto.DepartmentDto;
import primetest.dto.WardDto;
import primetest.impl.dao.WardDao;
import primetest.impl.dao.Transaction;

//@Stateless
public class WardDao implements Serializable {
	/**
	 *
	 */
	private static final long serialVersionUID = 6551770662358620809L;

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	private static List<WardDto> allItems = null;
	private static HashMap<String, WardDto> map = null;

	private DepartmentDao departmentDao;
	private BuildingDao buildingDao;
	private BedDao bedDao;

	@Inject
	public WardDao(DepartmentDao departmentDao, BuildingDao buildingDao,
			BedDao bedDao) {
		this.departmentDao = departmentDao;
		this.buildingDao = buildingDao;
		this.bedDao = bedDao;
	}

	public List<WardDto> getAllList(Transaction transaction) throws IOException {
		if (allItems != null) {
			return allItems;
		}
		PreparedStatement prepareStatement = null;
		ResultSet resultSet = null;
		map = new HashMap<String, WardDto>();
		try {

			Connection connection = transaction.getResource(Connection.class);
			prepareStatement = connection.prepareStatement(GET_ALL);
			resultSet = prepareStatement.executeQuery();

			while (resultSet.next()) {
				WardDto wardDto = makeWardDto(transaction, resultSet);
				map.put(wardDto.getKey(), wardDto);
			}
			allItems = new ArrayList<WardDto>(map.values());
			return allItems;
		} catch (SQLException e) {
			throw new IOException(e);
		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}

			if (prepareStatement != null) {
				try {
					prepareStatement.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}
		}
	}

	public List<WardDto> getAllListByBuildingAndDepartment(
			Transaction transaction, String buildingKey, int departmentSeq)
			throws IOException {
		if (allItems == null) {
			getAllList(transaction);
			logger.debug("find allItems == null in getAllListByBuildingAndDepartment");
		}
		List<WardDto> items = new ArrayList<WardDto>();
		for (WardDto wardDto : allItems) {
			if ((wardDto.getKey() == buildingKey || buildingKey == "")
					&& (wardDto.getDepartmentSeq() == departmentSeq || departmentSeq == -1)) {
				items.add(wardDto);
			}
		}
		return items;
	}

	private WardDto makeWardDto(Transaction transaction, ResultSet resultSet)
			throws SQLException, IOException {
		DepartmentDto departmentDto = departmentDao.findDepartment(transaction,
				resultSet.getInt(5));
		BuildingDto buildingDto = buildingDao.findBuilding(transaction,
				resultSet.getString(3));
		String[] bedKeys = resultSet.getString(6).split(":");
		List<BedDto> bedDtos = new ArrayList<BedDto>();
		for (int i = 0; i < bedKeys.length; i++) {
			BedDto bed = bedDao.findBed(transaction, bedKeys[i]);
			if (bed != null) {
				bedDtos.add(bed);
			}
		}
		String[] cons = resultSet.getString(7).split(":");
		Boolean[] conditions = new Boolean[cons.length];
		for (int i = 0; i < cons.length; i++) {
			if (cons[i] == "false") {
				conditions[i] = false;
			} else {
				conditions[i] = true;
			}
		}
		WardDto wardDto = new WardDto(resultSet.getString(1),
				resultSet.getString(2), buildingDto, resultSet.getInt(4),
				departmentDto, bedDtos, conditions, resultSet.getString(7));
		for (BedDto bedDto : bedDtos) {
			bedDto.setWardDto(wardDto);
		}
		return wardDto;
	}

	public void addWard(Transaction transaction, WardDto wardDto)
			throws IOException {
		PreparedStatement prepareStatement = null;
		ResultSet resultSet = null;
		allItems = null;

		try {
			Connection connection = transaction.getResource(Connection.class);
			prepareStatement = connection.prepareStatement(INSERT_ONE);
			prepareStatement.setString(1, wardDto.getKey());
			prepareStatement.setString(2, wardDto.getName());
			prepareStatement.setString(3, wardDto.getBuildingKey());
			prepareStatement.setInt(4, wardDto.getFloor());
			prepareStatement.setInt(5, wardDto.getDepartmentSeq());
			prepareStatement.setString(6, wardDto.getBeds());
			prepareStatement.setString(7, wardDto.getConditionsStr());
			prepareStatement.setString(8, wardDto.getExtraInfo());
			prepareStatement.executeUpdate();
			map.put(wardDto.getKey(), wardDto);
			allItems = new ArrayList<WardDto>(map.values());
			for (BedDto bed : wardDto.getBedDtos()) {
				bedDao.addBed(transaction, bed);
			}
		} catch (SQLException e) {
			throw new IOException(e);
		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}

			if (prepareStatement != null) {
				try {
					prepareStatement.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}
		}
	}

	public void updateWard(Transaction transaction, WardDto wardDto)
			throws IOException {
		PreparedStatement prepareStatement = null;
		ResultSet resultSet = null;
		allItems = null;

		try {
			Connection connection = transaction.getResource(Connection.class);
			prepareStatement = connection.prepareStatement(UPDATE_ONE);
			prepareStatement.setString(8, wardDto.getKey());
			prepareStatement.setString(1, wardDto.getName());
			prepareStatement.setString(2, wardDto.getBuildingKey());
			prepareStatement.setInt(3, wardDto.getFloor());
			prepareStatement.setInt(4, wardDto.getDepartmentSeq());
			prepareStatement.setString(5, wardDto.getBeds());
			prepareStatement.setString(6, wardDto.getConditionsStr());
			prepareStatement.setString(7, wardDto.getExtraInfo());
			prepareStatement.executeUpdate();
			map.put(wardDto.getKey(), wardDto);
			allItems = new ArrayList<WardDto>(map.values());
		} catch (SQLException e) {
			throw new IOException(e);
		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}

			if (prepareStatement != null) {
				try {
					prepareStatement.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}
		}

	}

	public void deleteWard(Transaction transaction, String wardKey)
			throws IOException {
		PreparedStatement prepareStatement = null;
		ResultSet resultSet = null;
		try {
			Connection connection = transaction.getResource(Connection.class);
			prepareStatement = connection.prepareStatement(DELETE_ONE);
			prepareStatement.setString(1, wardKey);
			prepareStatement.executeUpdate();
			WardDto dto = map.remove(wardKey);
			bedDao.deleteByWard(transaction, wardKey, dto.getBedDtos());
			allItems = new ArrayList<WardDto>(map.values());
		} catch (SQLException e) {
			throw new IOException(e);
		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}

			if (prepareStatement != null) {
				try {
					prepareStatement.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}
		}

	}

	public WardDto findWard(Transaction transaction, String key) throws IOException {
		WardDto deparmentDto = null;
		if(allItems != null){
			return map.get(key);
		}
		logger.debug("cached no functioned in findWard, key:" + key);
		PreparedStatement prepareStatement = null;
		ResultSet resultSet = null;

		try {
			Connection connection = transaction.getResource(Connection.class);
			prepareStatement = connection.prepareStatement(GET_BY_SEQ);
			prepareStatement.setString(1, key);
			resultSet = prepareStatement.executeQuery();

			if(resultSet.next()) {
				deparmentDto = makeWardDto(transaction, resultSet);
			}
			return deparmentDto;
		} catch (SQLException e) {
			throw new IOException(e);
		} finally {
			if (resultSet != null) {
				try {
					resultSet.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}

			if (prepareStatement != null) {
				try {
					prepareStatement.close();
				} catch (SQLException e) {
					logger.warn(e.getMessage(), e);
				}
			}
		}
	}


	private static final String GET_ALL = "select key_1, name, building_key, floor, department_seq, beds, conditions, extra_info from s_ward";
	private static final String INSERT_ONE = "insert into s_ward(key_1, name, building_key, floor, department_seq, beds, conditions, extra_info) values (?, ?, ?, ?, ?, ?, ?, ?)";
	private static final String UPDATE_ONE = "update s_ward set name = ?, building_key = ?, floor = ?, department_seq = ?, beds = ?, conditions = ?, extra_info = ? where key_1 = ?";
	private static final String DELETE_ONE = "delete from s_ward where key_1 = ?";
	private static final String GET_BY_SEQ = "select key_1, name, building_key, floor, department_seq, beds, conditions, extra_info from s_ward where key_1 = ?";


}
